package xyz.zq.sf.common.core.config;

import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.PackageVersion;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import xyz.zq.sf.common.core.util.LocalDateTimeUtil;

import java.io.IOException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * jackson 配置
 *
 * @author hzq
 * @date 2021-01-27 11:38
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
@AutoConfigureBefore(JacksonAutoConfiguration.class)
public class JacksonConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return jacksonObjectMapperBuilder -> {
            jacksonObjectMapperBuilder.locale(Locale.CHINA);
            jacksonObjectMapperBuilder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
            jacksonObjectMapperBuilder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
            jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
            /*
             * 当 Content-type: application/json
             * 对时间类型 序列化及反序列化 格式的转换
             */
            jacksonObjectMapperBuilder.modules(new SfLocalDateTimeModule());
        };
    }

    /**
     * 时间序列化格式转换
     */
    public static class SfLocalDateTimeModule extends SimpleModule {
        public SfLocalDateTimeModule() {
            super(PackageVersion.VERSION);
            this.addSerializer(Date.class, new DateSerializer(Boolean.TRUE, null));
            this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
            this.addSerializer(LocalDate.class, new LocalDateSerializer());
            this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));
            this.addDeserializer(Date.class, new DateDeserializer());
            this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
            this.addDeserializer(LocalDate.class, new LocalDateDeserializer());
            this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));
        }
    }

    /**
     * LocalDateTime 序列化 --> 时间戳
     */
    public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
        @Override
        public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers)
                throws IOException {
            if (value != null) {
                long timestamp = value.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
                gen.writeNumber(timestamp);
            }
        }
    }

    /**
     * LocalDate 序列化 --> 时间戳
     */
    public static class LocalDateSerializer extends JsonSerializer<LocalDate> {
        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers)
                throws IOException {
            if (value != null) {
                long timestamp = value.atStartOfDay(ZoneOffset.ofHours(8)).toInstant().toEpochMilli();
                gen.writeNumber(timestamp);
            }
        }
    }

    /**
     * Date 反序列化
     * 支持格式：
     * yyyy/MM/dd HH:mm:ss
     * yyyy/MM/dd
     * yyyy-MM-dd HH:mm:ss
     * yyyy-MM-dd
     * 时间戳
     */
    public static class DateDeserializer extends JsonDeserializer<Date> {
        @Override
        public Date deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            return LocalDateTimeUtil.parseStrToDate(p.getValueAsString(), Date.class);
        }
    }

    /**
     * LocalDateTime 反序列化
     * 支持格式：
     * yyyy/MM/dd HH:mm:ss
     * yyyy-MM-dd HH:mm:ss
     * 时间戳
     */
    public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            return LocalDateTimeUtil.parseStrToDate(p.getValueAsString(), LocalDateTime.class);
        }
    }

    /**
     * LocalDate 反序列化
     * 支持格式：
     * yyyy/MM/dd
     * yyyy-MM-dd
     * 时间戳
     */
    public static class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            return LocalDateTimeUtil.parseStrToDate(p.getValueAsString(), LocalDate.class);
        }
    }
}
